﻿using AutoMapper;
using System.Linq.Expressions;

namespace ETask.Api.Mappers
{
    public class GenericMapper<TSource, TDestination, TCreateDto, TUpdateDto>
      where TSource : class, new()
      where TDestination : class, new()
      where TCreateDto : class, new()
      where TUpdateDto : class, new()
    {
        private readonly IMapper _mapper;

        public GenericMapper(IMapper mapper)
        {
            _mapper = mapper;
        }

        // 源实体到目标DTO的映射
        public TDestination ToDto(TSource source)
        {
            return _mapper.Map<TDestination>(source);
        }

        // 源实体列表到目标DTO列表的映射
        public List<TDestination> ToDtoList(List<TSource> sources)
        {
            return _mapper.Map<List<TDestination>>(sources);
        }

        // 创建DTO到源实体的映射
        public TSource ToEntity(TCreateDto createDto)
        {
            return _mapper.Map<TSource>(createDto);
        }

        // 更新DTO到源实体的映射（仅更新非空字段）
        public void UpdateEntityFromDto(TUpdateDto updateDto, TSource entity)
        {
            _mapper.Map(updateDto, entity);
        }

        // 创建默认映射配置
        public static void CreateMapProfile(IMapperConfigurationExpression config)
        {
            // 源实体到目标DTO的映射
            config.CreateMap<TSource, TDestination>()
                  .PreserveReferences();

            // 目标DTO到源实体的映射（通常用于特殊场景）
            config.CreateMap<TDestination, TSource>()
                  .PreserveReferences();

            // 创建DTO到源实体的映射
            config.CreateMap<TCreateDto, TSource>()
                  .PreserveReferences();

            // 更新DTO到源实体的映射 - 忽略空值
            config.CreateMap<TUpdateDto, TSource>()
                  .PreserveReferences()
                  .ForAllMembers(opts => opts.Condition((src, dest, srcMember) =>
                      srcMember != null));
        }

        // 添加自定义映射规则的扩展方法
        public static IMappingExpression<TSource, TDestination> AddCustomMapping(
            IMapperConfigurationExpression config,
            Expression<Func<TSource, object>> sourceMember,
            Expression<Func<TDestination, object>> destMember,
            Func<TSource, object> mapFunc)
        {
            var mapping = config.CreateMap<TSource, TDestination>();
            return mapping.ForMember(destMember, opt => opt.MapFrom(src => mapFunc(src)));
        }
    }
}
